现有 api 和hooks 总结

React 稳定版常用 API & Hooks 总表(不含实验特性)

API / Hook作用基本用法常见场景面试高频点 / 注意事项
createElement创建 React 元素(JSX 底层)React.createElement('div', null, 'Hello')JSX 编译后底层实现JSX 本质不是 HTML,而是 createElement
Fragment避免额外 DOM 包裹<></>返回多个元素不生成真实 DOM
StrictMode开发模式检查潜在问题<React.StrictMode>检查副作用开发环境会触发额外 render
Suspense处理异步加载 fallback<Suspense fallback={<Loading />}>懒加载、数据加载常配合 lazy
lazy动态导入组件const A = lazy(() => import('./A'))路由拆包必须配合 Suspense
memo缓存组件,避免重复渲染memo(Component)性能优化浅比较 props
forwardRef父组件传 ref 给子组件forwardRef((props, ref)=>{})获取子组件 DOM常搭配 useImperativeHandle
createContext创建上下文const Ctx = createContext()跨层级传值避免 props drilling
createRef创建 ref(类组件常用)this.ref = createRef()DOM 操作函数组件通常用 useRef

基础 Hooks

Hook作用基本用法常见场景面试高频点 / 注意事项
useState状态管理const [count, setCount] = useState(0)表单、计数器异步更新、批处理
useEffect副作用处理useEffect(()=>{}, [])请求、监听、定时器清理函数防内存泄漏
useContext获取 Context 数据useContext(MyContext)全局主题、用户信息Provider value 改变会触发消费组件更新
useReducer复杂状态管理useReducer(reducer, init)多状态逻辑类似 Redux
useRef持久引用,不触发渲染const ref = useRef()DOM、缓存变量改变 .current 不 rerender

性能优化 Hooks

Hook作用基本用法常见场景面试高频点 / 注意事项
useMemo缓存计算结果useMemo(()=>fn(), [deps])大计算缓存值
useCallback缓存函数引用useCallback(()=>{}, [deps])子组件 props缓存函数
useTransition标记低优先级更新const [isPending, startTransition] = useTransition()搜索、过滤提升交互流畅度
useDeferredValue延迟值更新const deferred = useDeferredValue(value)输入搜索优化类似防抖但不是防抖

DOM / 生命周期 Hooks

Hook作用基本用法常见场景面试高频点 / 注意事项
useLayoutEffectDOM 更新后、绘制前执行useLayoutEffect(()=>{})测量 DOM会阻塞绘制
useImperativeHandle自定义 ref 暴露内容useImperativeHandle(ref, ()=>({...}))暴露子组件方法配合 forwardRef
useId生成稳定唯一 IDconst id = useId()表单 label/inputSSR 防止 hydration mismatch
useSyncExternalStore外部状态同步useSyncExternalStore(subscribe, getSnapshot)Redux/ZustandReact 18 外部状态标准
useInsertionEffect样式插入前执行CSS-in-JS 库使用styled-components很少业务直接用

React DOM API

API作用基本用法注意事项
createRootReact 18 新渲染入口createRoot(root).render(<App />)替代 ReactDOM.render
hydrateRootSSR hydrationhydrateRoot(el, <App />)Next.js SSR 常见
flushSync强制同步更新flushSync(()=>setX())谨慎使用

Hooks 使用规则(面试必考)

规则说明
只能在函数组件或自定义 Hook 顶层调用不能在循环/条件/嵌套函数里
必须保持调用顺序一致React 靠顺序记录 Hook
自定义 Hook 必须 use 开头规范 + lint 检查

高频对比(面试非常常见)

对比核心区别
useState vs useRefstate 改变会触发渲染;ref 不触发
useEffect vs useLayoutEffecteffect 异步;layoutEffect 同步阻塞
useMemo vs useCallbackmemo 缓存值;callback 缓存函数
Context vs ReduxContext 传值;Redux 是更强的状态管理
createRef vs useRefcreateRef 每次新建;useRef 持久

一句话记忆(适合面试口语)

面试官最爱问的坑点

  1. 为什么 useEffect 会执行两次?

因为 React StrictMode 开发模式故意检查副作用。

  1. 为什么 setState 不立即更新?

因为批处理 + 调度机制。

  1. 为什么不要滥用 useMemo/useCallback

因为本身也有性能成本。

  1. ref 为什么不触发更新?

因为它不是响应式状态。

 

最推荐你的记忆顺序(前端面试)

先背: useState → useEffect → useRef → useMemo → useCallback → useContext → useReducer

再背: forwardRef → useImperativeHandle → useTransition → useSyncExternalStore

对于大部分前端面试,这套已经覆盖 90%+ React 问题。

 

 

 

Q13: React 18 引入的 useTransition 是用来解决什么问题的?它和普通的 setState 有什么区别?

请你尽量从真实交互体验角度来解释,比如:

  1. 为什么需要“过渡更新”
  2. 什么是“卡顿 UI”问题
  3. useTransition 是如何改善体验的

  1. useTransition 解决的是什么问题?

👉 解决“非紧急更新导致 UI 卡顿”的问题

在 React 18 之前,所有 state 更新:

  1. 什么是卡顿 UI 问题?

例如:

问题:

👉 React 会一起执行 👉 UI 会卡顿(输入延迟)

  1. useTransition 解决思路

👉 把更新分成两类:

类型优先级
输入 / 点击高优先级
列表渲染低优先级(transition)
  1. 使用方式
  1. 它和 setState 的区别

setState:立即执行,高优先级

useTransition:标记为“可中断的低优先级更新”

React 可以:

  1. 核心效果

👉 提升“交互优先级”

用户感觉:

  1. isPending 的作用

👉 表示“后台更新还没完成”

常用于:

  1. 本质理解(面试加分点)

👉 React 18 引入的是:“并发渲染(Concurrent Rendering)思想”

useTransition 是:👉 “让 React 有选择地延迟非关键更新”


一句话总结:useTransition = “让重要更新先执行,让不重要更新慢慢来”


Standard Answer (English)

  1. What problem does useTransition solve?

It solves UI blocking caused by expensive or non-urgent state updates.

  1. What is the issue?

In traditional React:

  1. How useTransition helps

It splits updates into:

Non-urgent updates are marked as transitions and can be interrupted.

  1. Difference from setState
  1. Result

Improves perceived performance and keeps UI responsive.


面试口语版(中文)

useTransition 主要解决的是 UI 卡顿问题,比如输入框更新很快,但后面还有一个很重的列表过滤,如果用普通 setState 会一起卡住 UI。而 useTransition 可以把这个更新标记为低优先级,让 React 先处理用户输入,再慢慢更新列表,从而提升交互流畅度。

 

Interview Spoken Answer (English)

useTransition is used to solve UI blocking issues caused by heavy state updates. In traditional React, all updates have the same priority, so expensive rendering can block user input. useTransition allows us to mark some updates as low priority, so React can prioritize urgent interactions like typing or clicking, and handle heavy updates in the background, improving overall responsiveness.

 

 

Q14: useSyncExternalStore 是干什么用的?它解决了什么问题?在什么场景下你会用到它?

请重点从这几个角度回答:

  1. “external store” 指的是什么
  2. 它解决了 React 18 的什么并发问题
  3. 为什么普通 useState / useEffect 不够
  4. 实际开发中的典型例子(比如 Redux / Zustand / 浏览器 API)

  1. 什么是 external store?

👉 React 之外的状态源

也就是:不由 React 管理,但 UI 需要读取的数据。

例如:

  1. 它解决了什么问题?

👉 React 18 并发模式下的“数据不一致问题(tearing)”

什么是 tearing?

简单理解:👉 UI 在不同时间读取到了“不一致的状态版本”

例如:

👉 页面显示不一致

  1. 为什么 useState / useEffect 不够?

useState 问题:

useEffect 问题:

👉 在 concurrent rendering 下: useEffect 太晚了

  1. useSyncExternalStore 的作用

👉 让 React 安全订阅外部 store

它保证:

  1. 基本结构

参数解释:

subscribe:监听外部变化

getSnapshot:获取当前状态

  1. 实际例子

Redux(经典)

Redux 已经内部用它了:

浏览器 API

window size

Zustand(底层)

也是基于它实现订阅模型

  1. 核心本质理解

👉 它不是“状态 hook”

👉 它是:“React 和外部系统的同步桥梁”

  1. 为什么 React 18 必须有它?

因为 concurrent rendering:

👉 需要一个“强一致性订阅机制”


一句话总结:useSyncExternalStore = “安全连接 React 和外部状态系统的标准接口”


Standard Answer (English)

  1. What is an external store?

Any state source outside React, such as:

  1. What problem does it solve?

It solves state tearing issues in React 18 concurrent rendering, where different renders may see inconsistent snapshots of external state.

  1. Why not use useState/useEffect?
  1. What does useSyncExternalStore do?

It provides a safe way to subscribe to external stores while ensuring consistent snapshots during rendering.

  1. Example use cases

面试口语版(中文)

useSyncExternalStore 是 React 用来连接外部状态系统的 hook,比如 Redux、Zustand 或浏览器 API。它解决的问题是在 React 18 并发渲染下,外部状态可能在不同组件中读取不一致的问题。它通过订阅机制保证每次渲染拿到的是一致的状态快照,避免数据不一致。

 

Interview Spoken Answer (English)

useSyncExternalStore is a hook used to safely connect React with external state sources like Redux, Zustand, or browser APIs. It solves the problem of state tearing in React 18 concurrent rendering, where different components might read inconsistent snapshots of external state. It ensures that React always reads a consistent snapshot during rendering through a subscription-based mechanism.

 

Q15: useId 是做什么用的?它和自己手写 Math.random() 或递增 id 有什么区别?在 SSR(服务端渲染)场景下它解决了什么问题?

  1. useId 是做什么的?

useId 用来生成稳定、唯一的 ID

常见用途:

示例:

  1. 和 Math.random / 自增 id 的区别

❌ Math.random()

问题:

 

❌ 自增 id

问题:

 

✅ useId

特点:

  1. SSR 下解决什么问题?

解决 Hydration mismatch(服务端与客户端 DOM 不一致)

什么是 hydration mismatch?

SSR:

CSR:

👉 React 发现 DOM 不一致 → 报错或重新渲染

  1. useId 为什么不会错?

因为:

👉 React 在 服务端 + 客户端使用同一套 ID 生成规则

  1. 核心理解(面试加分)

useId 的本质不是“随机数生成器”,而是:“跨 SSR/CSR 的稳定标识系统”。

  1. 典型使用场景

一句话总结

👉 useId = “保证 SSR + CSR 一致的稳定唯一 ID”


Standard Answer (English)

  1. What is useId?

useId generates a stable and unique ID that remains consistent across renders and between server and client.

  1. Why not use Math.random or counters?
  1. What problem does it solve in SSR?

It solves hydration mismatch, where server-rendered HTML and client-rendered HTML have different IDs.

  1. Why useId works?

Because React generates IDs based on the component tree structure, ensuring consistency between server and client.


面试口语版(中文)

useId 用来生成稳定唯一的 ID,主要用于表单和无障碍场景。和 Math.random 或自增 ID 不同,它不会在每次 render 改变,而且在 SSR 场景下服务端和客户端生成的 ID 是一致的,可以避免 hydration mismatch 问题。

 

Interview Spoken Answer (English)

useId is used to generate stable and unique IDs, mainly for form associations and accessibility. Unlike Math.random or manual counters, it produces consistent IDs across renders and also ensures server and client output match in SSR, preventing hydration mismatches.

 

Q16: useImperativeHandle 是做什么用的?它和 forwardRef 是什么关系?在真实项目中你会在什么场景用它?

请重点回答:

  1. 它解决了什么问题(为什么不能直接用 ref)
  2. 和 forwardRef 的配合关系
  3. 一个真实业务场景(比如表单 / 弹窗 / 组件控制)

  1. useImperativeHandle 是做什么的?

👉 用来自定义子组件通过 ref 暴露给父组件的方法或属性

正常情况下:

👉 直接拿到的是 DOM 或组件实例(有限)

但有时候你不想暴露整个 DOM 或组件内部结构。

  1. 它解决了什么问题?

👉 控制“子组件对外暴露的 API”

也就是:

不让父组件随便操作内部,只暴露必要方法

  1. 和 forwardRef 的关系

forwardRef:👉 让函数组件“能接收 ref”

useImperativeHandle:👉 定义“ref 到底暴露什么”

例子:父组件需要控制子组件里的 Input 焦点或弹窗(Modal)的开关,其余的方法不需要暴露给父组件。

为什么要这么做?(对比直接使用 forwardRef

  1. 为什么不能直接用 ref?

如果不用它:

问题:

👉 React 推荐: “只暴露必要 API,而不是整个内部结构”

  1. 真实项目场景

场景1:表单组件(非常常见)

场景2:弹窗控制

场景3:输入框聚焦

但可以只暴露 focus,而不暴露 DOM 全部能力

  1. 本质理解(面试加分)

👉 它不是“操作 ref 的 hook”

👉 而是:“组件对外 API 封装机制”

 

注意事项


一句话总结:useImperativeHandle = “控制 ref 暴露范围,让组件像 API 一样被调用”


Standard Answer (English)

  1. What is useImperativeHandle?

It allows you to customize what a parent component can access through a ref.

  1. Problem it solves

Without it, a ref exposes the entire DOM or component instance, which breaks encapsulation.

  1. Relationship with forwardRef
  1. Real-world use cases

面试口语版(中文)

useImperativeHandle 是用来控制父组件通过 ref 能访问子组件哪些方法的。它通常和 forwardRef 一起使用,forwardRef 是让函数组件能接收 ref,而 useImperativeHandle 是用来定制 ref 暴露的内容,比如只暴露 open、close 或 focus 方法,而不是整个 DOM 或组件实例,这样可以更好地封装组件。

 

Interview Spoken Answer (English)

useImperativeHandle is used to control what a parent component can access through a ref. It works together with forwardRef, where forwardRef allows a functional component to receive a ref, and useImperativeHandle defines what methods or properties are exposed. This is useful for encapsulation, for example exposing only methods like open, close, or focus in modal or form components instead of exposing the entire internal instance.

 

Q17: React 中“受控组件(controlled component)”和“非受控组件(uncontrolled component)”有什么区别?

请重点回答:

  1. 什么是受控 / 非受控
  2. 它们和 useState / useRef 的关系
  3. 各自适合什么场景(真实项目)
  4. 为什么 React 推荐受控组件更多一些

  1. 什么是受控组件(Controlled Component)

👉 表单数据由 React state 完全控制

特点:

  1. 什么是非受控组件(Uncontrolled Component)

👉 表单数据由 DOM 自己管理

获取值:

特点:

  1. 和 useState / useRef 的关系
类型Hook
受控组件useState
非受控组件useRef
  1. 核心区别(面试重点)
对比受控组件非受控组件
数据来源React stateDOM
更新方式onChange → setStateDOM 自己更新
可控性
性能可能稍差更快
  1. 各自适合什么场景?

受控组件适合:

 

非受控组件适合:

  1. 为什么 React 更推荐受控组件?

👉 因为:

但缺点是:


一句话总结

👉 受控组件 = React 管数据 👉 非受控组件 = DOM 管数据


Standard Answer (English)

  1. Controlled component

A controlled component is where form data is fully managed by React state.

  1. Uncontrolled component

An uncontrolled component stores its state in the DOM, and React accesses it via ref when needed.

  1. Key difference
ControlledUncontrolled
React stateDOM
onChange updates stateDOM handles updates
predictableless controlled
  1. Use cases

Controlled:

Uncontrolled:

  1. Why React prefers controlled

Because it provides:


面试口语版(中文)

受控组件就是表单数据由 React 的 state 来控制,每次输入都会更新 state,从而驱动 UI。非受控组件则是数据存在 DOM 里,通过 ref 去读取。React 更推荐受控组件,因为数据流更清晰、可控,也更容易做校验和联动逻辑。

 

Interview Spoken Answer (English)

A controlled component is where the form state is managed by React state, so every input change updates the state and keeps UI in sync. An uncontrolled component stores its value in the DOM, and we access it via refs when needed. React prefers controlled components because they provide a predictable data flow, easier debugging, and better control over form logic and validation.

 

 

Q18: React Hooks 里“依赖项(deps)”到底是如何判断变化的?是深比较还是浅比较?为什么这样设计?

请重点回答:

  1. React 是怎么比较依赖项的
  2. 为什么不用深比较
  3. 这种设计会带来什么常见问题(比如对象/数组)
  4. 实际开发中如何避免坑(useMemo/useCallback等)

  1. React 如何判断 deps 是否变化?

👉 使用 浅比较(shallow comparison)

底层使用的是:Object.is

示例:

React 做的是:

结论:

👉 比较的是引用是否变化(reference equality)

不是内容比较

  1. 为什么不用深比较?

这是面试重点 ⚠️

❌ 如果用深比较:

问题1:性能极差

对象可能很大:

👉 每次 render 都要遍历整个结构

 

问题2:无法预测成本

React render 应该是:

O(1) 或 O(n)

深比较可能变成:

O(n²) 或更糟

 

问题3:函数/循环引用复杂

👉 深比较非常不可靠

  1. 这种设计带来的问题

❗ 1. 对象/数组“误触发更新”

每次 render: 👉 都是新对象引用 👉 effect 每次都会执行

 

❗ 2. 函数依赖问题

👉 每次都是新函数引用

 

❗ 3. 容易导致无限循环

  1. 如何解决这些问题?

✅ 1. useMemo(稳定对象)

✅ 2. useCallback(稳定函数)

✅ 3. 拆分依赖(不要传大对象)

✅ 4. ESLint hooks plugin

自动提醒 missing deps

  1. 本质理解(面试加分)

👉 React 不关心“内容是否相等”

👉 它只关心:“这一轮 render 的引用有没有变化”


一句话总结:deps 比较是 Object.is 的浅比较,本质是“引用是否变化”,不是内容比较。


Standard Answer (English)

  1. How does React compare dependencies?

React uses shallow comparison with Object.is, meaning it compares reference equality, not deep value equality.

  1. Why not deep comparison?
  1. Common issues caused
  1. How to solve it

面试口语版(中文)

React 在比较依赖项的时候用的是浅比较,也就是 Object.is,它只判断引用是否变化,而不是内容是否相等。因为如果做深比较性能会非常差,而且无法预测复杂数据结构的成本。所以如果在依赖里传对象或者函数,每次 render 都会产生新的引用,就会导致 effect 频繁触发,这时候一般会用 useMemo 或 useCallback 来保持引用稳定。

 

Interview Spoken Answer (English)

React uses shallow comparison with Object.is to check whether dependencies have changed, meaning it compares references instead of deep values. Deep comparison is not used because it would be too expensive and unpredictable for complex data structures. As a result, objects or functions created during render will often change references and retrigger effects, which is why we use useMemo and useCallback to stabilize them.

 

 

Q19: React Hooks 里“状态更新是异步的”是什么意思?为什么 setState 之后不能立刻拿到最新值?

请重点回答:

  1. 什么叫“异步更新”(不是 Promise 那种异步)
  2. React 为什么要这么设计(批处理 / 性能)
  3. 代码里会出现什么现象(常见 bug)
  4. 如何正确获取最新值(实战方式)

  1. 什么叫“setState 是异步的”?

这里的“异步”不是 Promise / async...await,而是指:React 会把状态更新先加入队列(batching),不会立刻修改 state

示例:

👉 log 还是旧值

  1. 为什么不能立即更新?

因为 React 18做了一个优化:👉 批处理(Batching)

如果每次 setState 都立刻更新:

👉 性能爆炸

 

React 的做法:

👉 把多个 setState 合并成一次 render

  1. React 的更新流程

简化理解:

  1. 会出现什么“坑”?

❗ 1. 立即读取旧值

❗ 2. 依赖旧 state 更新错误

👉 可能只 +1

  1. 正确写法(非常重要)

✔ 函数式更新

👉 才会 +2

  1. 如何获取最新值?

方法1:useEffect

方法2:useRef(同步场景)

方法3:函数式 setState(依赖旧值)

  1. React 为什么这样设计?

👉 核心目标:


一句话总结:setState “异步”指的是 React 会延迟 + 合并更新,而不是立即修改 state


Standard Answer (English)

  1. What does “async state update” mean?

It does NOT mean Promise-based async. It means React batches state updates and applies them later during rendering.

  1. Why does React do this?
  1. What problems does it cause?
  1. How to handle it correctly?

面试口语版(中文)

setState 的“异步”并不是 Promise 的异步,而是 React 会把多个状态更新先放到队列里统一处理,做批量更新来优化性能,所以在调用 setState 之后不能立刻拿到最新值。如果想基于最新状态更新,应该使用函数式 setState,或者通过 useEffect 来监听更新后的值。

 

Interview Spoken Answer (English)

The “async” nature of setState does not mean Promise-based async. It means React batches multiple state updates together and applies them during rendering for performance optimization. Because of this batching, we cannot immediately access the updated state after calling setState. To correctly update based on the latest state, we should use functional updates or rely on useEffect after state changes are committed.

 

Q20: React Hooks 里“为什么不能在条件语句中调用 Hooks”这个规则背后的真正设计原因是什么?

请从更底层理解回答:

  1. React 是如何“识别每个 Hook 属于哪个 state 的”
  2. 如果允许条件调用,会破坏什么机制
  3. 为什么 React 不用“变量名或 key”来绑定 Hook

(这是一个偏原理 + 架构理解题)


  1. React 是如何识别 Hook 属于哪个 state 的?

👉 React 用的是:“调用顺序(call order index)”来绑定 Hook

本质机制:

每个组件在 render 时,React 会维护一个“Hook 指针”:

👉 Hook 和 state 是靠“顺序位置”绑定的,而不是名字。

  1. 如果允许条件调用,会发生什么?

❌ 错误示例:

问题:

不同 render 会变成:

render 1:

render 2(flag=false):

👉 React 无法知道:

  1. 会破坏什么机制?

👉 会破坏 React 的核心设计:Hook 的稳定映射关系(stable hook ordering)

结果:

  1. 为什么不用“变量名 / key”绑定hooks的顺序?

这是这题的高分点 ⚠️

❌ 方案1:用变量名

问题:

❌ 方案2:用 key

问题:

❌ 更关键问题:

React 每次 render 是“重新执行函数”,没有稳定身份系统。

  1. 为什么“顺序方案”是最优解?

因为:

✔ 简单

✔ O(1) 访问

✔ 不需要额外结构

✔ 适配函数组件执行模型

👉 React 选择了最朴素但最稳定的方案:

“用执行顺序作为唯一标识”

  1. React 如何防止错误?

在开发模式:

👉 React 会记录 Hook 调用顺序

如果发现:

👉 直接报错:


一句话总结

👉 Hooks 依赖“调用顺序”来绑定 state,因为 React 没有办法在函数执行中稳定识别变量身份


Standard Answer (English)

  1. How does React identify each Hook?

React uses call order indexing to associate hooks with state.

Each render maintains a pointer that maps:

  1. What happens if hooks are conditional?

Hook order changes between renders, causing:

  1. Why not use names or keys?

Because:

  1. Why order-based system?

Because it is:


面试口语版(中文)

React 之所以不能在条件语句里调用 Hooks,是因为它是通过“调用顺序”来区分每个 Hook 对应的 state 的,而不是通过变量名或者 key。如果在 if 或循环里调用 Hook,会导致不同 render 的调用顺序不一致,从而造成 state 错位。React 也没有办法在函数执行时稳定识别 Hook 的身份,所以只能依赖顺序这种最稳定的方式。

 

Interview Spoken Answer (English)

React cannot allow hooks inside conditions because it relies on the order of hook calls to associate state with each hook. If hooks are conditionally executed, the order will change between renders, causing state mismatches. React does not use variable names or keys because they are not reliable or stable during function execution, so the only consistent way is to use call order as the identifier.

 

 

Q21: 自定义 Hook(Custom Hook)是什么?它和普通函数有什么区别?为什么必须以 use 开头?

请重点回答:

  1. 自定义 Hook 解决什么问题
  2. 它和普通函数的本质区别
  3. 为什么必须以 use 开头(规则 + ESLint + React 机制)
  4. 一个真实项目中的例子(比如抽取逻辑)

  1. 自定义 Hook 是什么?

本质:封装可复用的 Hook 逻辑函数

它解决的问题:逻辑复用(logic reuse)

比如:

  1. 和普通函数的本质区别

这是核心考点 ⚠️

普通函数:

特点:

 

自定义 Hook:

特点:

 

本质区别一句话:

👉 自定义 Hook = “带状态的逻辑复用单元”

  1. 为什么必须以 use 开头?

这是面试重点 ⚠️

原因1:React Hook 规则识别

React 内部通过“函数名是否以 use 开头”,来判断这个函数是否可能包含 Hook。

原因2:ESLint 检查

会强制要求:

👉 useXXX 才能调用 Hook

原因3:防止错误使用

例如:

React 无法检测 → 会报错或逻辑错乱

  1. React 为什么需要这个规则?

因为:

👉 React 无法在运行时判断“普通函数里是否用了 Hook”

所以只能用:

命名约定 + lint 规则

  1. 真实项目例子

场景1:请求封装

场景2:防抖

场景3:订阅事件

  1. 本质理解(面试加分)

👉 自定义 Hook = “逻辑组件化”

不是 UI 组件,而是:state + effect + logic 的复用单元


一句话总结:自定义 Hook 是把 React 逻辑抽成可复用函数,但本质仍然依赖 Hook 体系运行


Standard Answer (English)

  1. What is a custom Hook?

A custom Hook is a function that starts with use and allows reuse of React stateful logic.

  1. Difference from normal functions
  1. Why must it start with "use"?

Because:

  1. Real-world usage

面试口语版(中文)

自定义 Hook 本质是把 React 的状态逻辑和副作用逻辑抽成可复用的函数,它可以使用 useState 和 useEffect,而普通函数不行。必须以 use 开头是因为 React 和 ESLint 会通过这个规则来识别这个函数是否包含 Hook,从而保证 Hook 的调用规则不被破坏。常见场景包括请求封装、防抖、订阅事件等。

 

Interview Spoken Answer (English)

A custom Hook is a function that allows us to reuse React stateful logic using Hooks like useState and useEffect. Unlike normal functions, custom Hooks can participate in React’s lifecycle. They must start with "use" because React and ESLint rely on this naming convention to identify Hook usage and enforce the rules of Hooks, preventing incorrect usage. Common use cases include data fetching, debounce logic, and event subscriptions.

 

Q22: React 中“副作用(side effect)”到底是什么?为什么要用 useEffect 来管理它,而不能直接在 render 里做?

请重点回答:

  1. 什么是副作用(用直观例子说明)
  2. render 阶段的职责是什么
  3. 如果在 render 里写副作用会有什么问题
  4. useEffect 的设计意义(时机 + React 生命周期)

  1. 什么是副作用(side effect)?

👉 简单理解:所有“影响 React 之外的操作”都是副作用

常见副作用:

  1. render 阶段的职责是什么?

👉 React 的 render 应该是:纯函数(pure function)

规则:

理想模型:

  1. 如果在 render 里写副作用会发生什么?

❌ 示例:

问题:

  1. useEffect 的设计意义

👉 React 把副作用放到:render 之后执行

生命周期顺序(简化):

为什么这样设计?

✔ 保证 UI 先稳定显示

✔ 不阻塞渲染

✔ 副作用可控执行

  1. useEffect 的本质

它不是“副作用工具”,而是“render 完成后的安全执行区”

  1. 核心设计哲学(面试加分)

React 分成两部分:

阶段职责
render计算 UI
effect处理外部系统

👉 这是 React 的核心架构分离


一句话总结:副作用是“影响外部世界的操作”,必须放在 useEffect,因为 render 必须保持纯函数


Standard Answer (English)

  1. What is a side effect?

A side effect is any operation that affects something outside React, such as:

  1. What is the role of render?

The render phase must be pure:

UI = f(state)

It should not cause side effects or modify external systems.

  1. What happens if we put side effects in render?
  1. Why use useEffect?

useEffect runs after the render is committed to the DOM, ensuring:


面试口语版(中文)

副作用就是所有会影响 React 之外的操作,比如请求接口、操作 DOM 或定时器。React 的 render 阶段应该是纯函数,只负责根据 state 计算 UI,如果在 render 里写副作用,会导致每次渲染都重复执行,甚至可能造成死循环。所以 React 把副作用放在 useEffect 里,让它在 DOM 更新完成之后再执行,这样可以保证 UI 先稳定渲染,再处理外部逻辑。

 

Interview Spoken Answer (English)

A side effect is any operation that affects something outside of React, such as API calls, DOM manipulation, or timers. The render phase in React must remain pure and only calculate the UI based on state. If we put side effects inside render, it would cause repeated executions and unpredictable behavior. That’s why React uses useEffect, which runs after the DOM is updated, ensuring the UI is rendered first before handling external operations.

 

 

 

Q23:React 中的“闭包陷阱(stale closure)”是什么?为什么会出现?

一、标准答案(中文)

  1. 什么是闭包陷阱?

闭包陷阱指的是:在异步或延迟执行的函数中,拿到的是“旧的 state / props”,而不是最新值。

  1. 典型例子(setTimeout)

二、为什么会发生?

核心原因:

👉 JavaScript 闭包机制,函数会“记住”它创建时的变量环境。

而 React 中:

三、如何解决闭包陷阱?

  1. 使用函数式更新(推荐)
  1. 使用 ref(保存最新值)
  1. 重新订阅 effect(依赖数组)

五、面试核心总结一句话

“闭包陷阱的本质是:函数捕获的是创建时的 state,而不是运行时的 state。”


English Version (Interview Answer)

  1. What is stale closure?

A stale closure happens when a function captures and uses outdated state or props instead of the latest values.

  1. Example

In async code like setTimeout:

It logs the value of count at the time the function was created, not the latest value.

  1. Why does it happen?

Because of JavaScript closures:

  1. How to fix it?
  1. Key idea

A closure always “remembers” the value from when it was created, not when it is executed.


中文口语版

闭包陷阱指的是,在异步函数或者定时器里面拿到的是旧的 state,而不是最新的值。

本质原因是 JavaScript 的闭包机制,每次 render 都会生成一份新的 state,但是异步函数捕获的是创建时那一份变量。

比如 setTimeout 或 useEffect 里面,如果依赖没写好,就会拿到旧值。

解决方式一般有三种:用函数式更新、用 useRef 保存最新值,或者把 state 放进依赖数组里。

 

English spoken version

A stale closure happens when an async function uses outdated state instead of the latest one.

The root cause is JavaScript closures. Each render creates a new scope, but async callbacks capture the variables from the time they were created.

So in cases like setTimeout or useEffect, you may see stale values.

To fix it, we can use functional updates, useRef for latest value, or properly manage dependencies.

 

Q24:useState 的“函数式更新”是什么?什么时候必须用?

一、标准答案(中文)

  1. 什么是函数式更新?

函数式更新指的是:

而不是:

二、两者的本质区别

❌ 非函数式写法:

👉 使用的是“当前闭包里的 count”

✅ 函数式写法:

👉 使用的是“React 最新的 state 值”

 

三、关键问题:为什么会出 bug?

React state 更新是异步 + 批处理(batching)的。

举例:

👉 你以为 +3 👉 实际可能只 +1

因为:这 3 次用的是同一个旧 count

 

四、正确写法(函数式更新)

👉 一定 +3

 

五、什么时候必须用函数式更新?

  1. 多次连续更新 state
  1. 依赖前一个 state

例如:

  1. 异步场景(非常重要)

👉 应该:

 

六、React 为什么要这样设计?

核心原因:

React state 更新是 基于队列(queue)批处理的,它不保证你拿到的是“最新 state”。

函数式更新提供:

✔ 可靠性 ✔ 避免闭包旧值问题 ✔ 支持并发渲染(concurrent rendering)


English Version (Interview Answer)

  1. What is functional update?

Instead of:

  1. Key difference

❌ Direct value update:

Uses the value from current closure.

✅ Functional update:

Uses the latest state value provided by React.

  1. Why can bugs happen?

React state updates are batched and asynchronous.

So:

may only increase once.

  1. Correct approach

Ensures each update is based on the latest state.

  1. When must we use it?
  1. Why React designed it?

Because React batches updates and cannot guarantee immediate state consistency, functional updates ensure correctness and avoid stale closures.


中文口语版

函数式更新就是 setState 传一个函数,比如 setCount(prev => prev + 1)。

它和直接写 setCount(count + 1) 最大区别是,前者拿到的是 React 最新的 state,而后者用的是当前闭包里的旧值。

在 React 里 state 更新是异步并且会批处理的,所以如果你连续调用多次 setCount(count + 1),可能只会生效一次。

但是如果用函数式更新,每次都会基于最新值,所以可以保证结果正确。

一般在连续更新 state、依赖上一次 state 或者异步场景下,我都会用函数式更新。

 

English spoken version

Functional update means passing a function to setState, like setCount(prev => prev + 1).

The difference is that the normal way uses the value from the current closure, while functional update always uses the latest state provided by React.

Because state updates are asynchronous and batched, multiple updates using the same value may not work correctly.

So in cases like consecutive updates, async callbacks, or when the new state depends on the previous one, I always use functional updates to ensure correctness.

 

 

Q25:React 组件重新渲染(re-render)的触发条件有哪些?如何避免不必要的 re-render?

一、标准答案(中文)

  1. 组件重新渲染的触发条件

React 组件发生 re-render 主要有 4 种情况:

(1)state 变化

(2)props 变化

(3)context 变化

(4)父组件 re-render(间接触发)

 

二、关键面试点(容易加分)

❗ 父组件 re-render ≠ 子组件一定有变化

但: 👉 子组件函数一定会重新执行(默认情况下)

 

三、如何避免不必要的 re-render?

  1. React.memo
  1. useMemo(缓存计算结果)
  1. useCallback(缓存函数引用)
  1. 拆分组件(非常重要)
  1. 状态下沉 / 状态隔离
  1. Context 优化

 

四、常见面试坑

❌ 误区1:

“子组件 props 没变就不会 re-render”

👉 错,父组件 re-render ,默认子组件也会执行 render

❌ 误区2:

“useMemo / useCallback 可以阻止 re-render”

👉 错,它们只是优化引用,不是阻止 render 的核心手段


English Version (Interview Answer)

  1. When does a React component re-render?

A component re-renders in these cases:

(1) State change

When useState or setState updates, React triggers a re-render.

(2) Props change

When parent re-renders and passes new props, child components re-render.

(3) Context change

When a value in useContext changes, all consuming components re-render.

(4) Parent re-render

Even if props don’t change, children still re-render by default.

  1. Key insight

A parent re-render does NOT guarantee meaningful changes in children, but it still causes child render execution.

  1. How to prevent unnecessary re-renders?

中文口语版

React 组件重新渲染主要有几种情况。

第一是 state 变化,比如 useState 更新就会触发 re-render。

第二是 props 变化,父组件更新后子组件会重新渲染。

第三是 context 变化,useContext 依赖的值变了也会触发更新。

还有一个很重要的点是,即使 props 没变,只要父组件 re-render,子组件默认也会重新执行 render。

优化方面,我一般会用 React.memo 来避免不必要的渲染,用 useMemo 和 useCallback 来稳定引用,然后通过拆组件和拆 state 来减少影响范围。

 

English spoken version

A React component re-renders mainly in four cases.

First, when state changes, like useState updates.

Second, when props change from the parent component.

Third, when context value changes, all consuming components re-render.

And also, even if props don’t change, a child component will still re-render when its parent re-renders by default.

To optimize this, I usually use React.memo, useMemo and useCallback to stabilize references, and also split components and state to reduce unnecessary re-renders.

 

 

Q26:为什么 useState 不能在函数外用?Hooks 依赖的到底是什么机制?

一、标准答案(中文)

  1. useState 为什么不能在函数外调用?

因为:Hooks 必须在 React 组件的“渲染过程(render phase)”中执行。

而函数外:

所以会报错:Invalid hook call

 

二、核心本质:Hooks 依赖“当前组件实例”

React 内部维护了:👉 Fiber Node(组件实例结构)

每个组件在 React 内部都有一份:

 

三、Hooks 是怎么工作的?

当组件执行时:

React 实际做的是:

  1. 找到当前 fiber
  2. 找到该组件的 hooks list
  3. 按“调用顺序”存取 hook

 

四、为什么不能在函数外用?

函数外:

问题是:

❌ 没有 fiber

❌ 没有 render 上下文

❌ 没有 hook 顺序链

❌ React 无法挂载状态

 

五、为什么 Hooks 必须“按顺序调用”?

React 用的是: 链表 + index 顺序匹配

例如:

React 不是用名字匹配,而是:

👉 第1个 hook、第2个 hook、第3个 hook

如果你放在 if 里:

👉 hook 顺序就乱了 👉 React 找不到正确 state

 

六、一句话总结(面试杀手级)

👉 Hooks 依赖的是 React 的“Fiber 渲染上下文 + 调用顺序机制”,脱离 render 环境就无法工作。


English Version (Interview Answer)

  1. Why can't we use useState outside a function component?

Because hooks must run inside React's render process.

Outside a component:

So React throws an error: invalid hook call.

  1. Core mechanism

React stores component state inside a structure called Fiber Node.

Each component instance has:

  1. How hooks work

Hooks are matched by call order, not by name.

So React relies on consistent execution order during render.

  1. Why outside function doesn't work?

Because there is:

So React cannot attach state anywhere.

  1. Key idea

Hooks only work inside React render because they depend on internal fiber and sequential execution order.


中文口语版

useState 不能在函数外使用的原因是,Hooks 必须运行在 React 的渲染流程里面。

React 内部是通过 Fiber 结构来管理每个组件的 state 的,每个组件都有自己的 hooks 链表,并且是按调用顺序来存储的。

如果你在函数外调用 useState,就没有组件实例,也没有 fiber,React 根本不知道这个 state 属于谁。

所以 Hooks 必须依赖组件的执行上下文,而且必须按顺序调用,否则 React 就会乱掉。

 

English spoken version

We can’t use useState outside a function component because hooks must run inside React’s render process.

React uses a Fiber structure to manage each component’s state, and hooks are stored in a list based on call order.

Outside a component, there is no fiber instance and no render context, so React has no way to track the state.

That’s why hooks must run inside components and follow a consistent call order.